package cn.coufran.springboot.starter.auth.impl.token;

import cn.coufran.commons.exception.ServiceException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;
import java.util.UUID;

/**
 * 基于Jjwt的Token注册机，重启后秘钥重置，所有Token失效
 * @author Coufran
 * @version 1.0.0
 * @since 1.0.0
 */
public class JjwtTokenRegister implements TokenRegister {
    /** 签名算法 */
    private static final String ALGORITHM = "HmacSHA256";
    /** 负载数据键 */
    public static final String CLAIM_KEY_DATA = "data";
    /** Jjwt秘钥 */
    private final SecretKey KEY;

    /**
     * 构造注册机，生成秘钥
     */
    public JjwtTokenRegister() {
        try {
            KEY = KeyGenerator.getInstance(ALGORITHM).generateKey();
        } catch (NoSuchAlgorithmException e) {
            throw new ServiceException(e);
        }
    }

    /**
     * 给定秘钥，构造注册机
     * @param key 秘钥
     */
    public JjwtTokenRegister(byte[] key) {
        KEY = new SecretKeySpec(key, ALGORITHM);
    }

    /**
     * 获取秘钥
     * @return 秘钥
     */
    public byte[] getKey() {
        return KEY.getEncoded();
    }

    /**
     * 生成Token
     * @param payload Token负载
     * @param expireTime 有效
     * @return Token
     */
    @Override
    public String generate(String payload, int expireTime) {
        HashMap<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_DATA, payload);
        Date now = new Date();
        return Jwts.builder()
                .claims(claims)
                .issuedAt(now)
                .expiration(new Date(now.getTime() + expireTime * 60L * 1000))
                .id(UUID.randomUUID().toString())
                .signWith(KEY)
                .compact();
    }

    /**
     * 校验并解析Token
     * @param token Token
     * @return Token负载，解析失败或Token过期等异常返回null
     */
    @Override
    public String parse(String token) {
        try {
            return Jwts.parser()
                    .verifyWith(KEY)
                    .build()
                    .parseSignedClaims(token)
                    .getPayload()
                    .get(CLAIM_KEY_DATA, String.class);
        } catch (JwtException e) {
            return null;
        }
    }
}
